package jm.demo.je;

import java.io.File;

import com.sleepycat.bind.ByteArrayBinding;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.collections.StoredSortedMap;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;

public class JeUtil {
    
    public static ByteArrayBinding BYTE_ARRAY_BINDING = new ByteArrayBinding();

    public static <K, V> JeConnection<K, V> open(JeConfig<K, V> config) {
        // String dir = config.getDir();, String db_name, Class<K> key_class, Class<V> value_class, boolean force_create, boolean sorted;
        StoredMap<K, V> map = null;
        // environment is transactional
        EnvironmentConfig envConfig = new EnvironmentConfig();
        envConfig.setNodeName(config.getNodeName());
        envConfig.setTransactional(true);
        if (config.isForceCreate()) {
            envConfig.setAllowCreate(true);
            // create the directory if it does not exist
            File file = new File(config.getDir());
            file.mkdirs();
        }

        Environment env = new Environment(new File(config.getDir()), envConfig);



        // use a generic database configuration
        DatabaseConfig dbConfig = new DatabaseConfig();
        if(config.isTransactional()) {
            dbConfig.setTransactional(true);
        } else {
            if(config.isDeferredWrite()) {
                dbConfig.setDeferredWrite(true);
            }
        }
//        dbConfig.setDeferredWrite(true);
        if (config.isForceCreate()) {
            dbConfig.setAllowCreate(true);
        }

        // catalog is needed for serial bindings (java serialization)
        Database catalog_db = null; // = env.openDatabase(null, "catalog", dbConfig);
        StoredClassCatalog catalog = null;

        // use Integer tuple binding for key entries
        EntryBinding<K> key_entry_binding = findEntryBinding(config.getKeyClass());
        if(key_entry_binding == null) {
            catalog_db = env.openDatabase(null, "catalog", dbConfig);
            catalog = new StoredClassCatalog(catalog_db);
            key_entry_binding = new SerialBinding<K>(catalog, config.getKeyClass());
        }
        

        // use String serial binding for data entries
        EntryBinding<V> value_entry_binding = findEntryBinding(config.getValueClass());
        if(value_entry_binding == null) {
            if(catalog_db == null) {
                catalog_db = env.openDatabase(null, "catalog", dbConfig);
            }
            if(catalog == null) {
                catalog = new StoredClassCatalog(catalog_db);
            }
            value_entry_binding = new SerialBinding<V>(catalog, config.getValueClass());
        }

        Database db = env.openDatabase(null, config.getDbName(), dbConfig);

        // create a map view of the database
        if(config.isSorted()) {
            map  = new StoredSortedMap<K, V>
                (db, key_entry_binding, value_entry_binding, !config.isReadonly());
        } else {
            map  = new StoredMap<K, V>
                (db, key_entry_binding, value_entry_binding, !config.isReadonly());
        }

        return new JeConnection<K, V>(env, db, catalog, map);
    }
    
    @SuppressWarnings("unchecked")
    public static <T> EntryBinding<T> findEntryBinding(Class<T> clazz) {
        EntryBinding<T> result; 
        result = TupleBinding.getPrimitiveBinding(clazz);
        if(result != null) return result;
        if(byte[].class.equals(clazz)) {
            return  (EntryBinding<T>)BYTE_ARRAY_BINDING;
        }
        
        return null;
    }
    
    
}
